import math
import sys


#-----------------------------------------------------------------------------#
SAMPLE_CORRECTION = 1.0
MIN_PROBABILITY = 0.001
#-----------------------------------------------------------------------------#

class NBM(object):
    """
    Cette classe implemente un classificateur bayesien naive.
    """
    def __init__(self, matrix, clusters, words, matrix2):
        self.featuresMatrix = matrix
        self.clusters = clusters
        self.words = words
        self.matrix2 = matrix2
        self.positions = self.findClustersPositions()
        self.nbWords = self.countWordsInAllClass()
        self.nbWordsInCls = self.getNbWordsInClusters()


    def findClustersPositions(self):
        pos = {}
        for i in range(self.featuresMatrix.shape[0]):
            if pos.has_key(self.featuresMatrix[i, 0]):
                pos[self.featuresMatrix[i, 0]].append(i)
            else:
                pos[self.featuresMatrix[i, 0]] = [i]
        
        return pos


    def classify(self, featuresVector):
        """
        Cette fonction cherche la categorie qui maximise 
        P(Category|Words) 
        """
        max = -sys.maxint
        for cID in range(self.clusters.nbClusters):
            m = self.p_of_C_knowing_FeaturesVector(featuresVector, cID)
            if m > max:
                max = m
                candidate = cID
        return candidate        
                
    
    def p_of_C_knowing_FeaturesVector(self, featuresVector, cID):
        p_of_c = float(self.positions[cID].__len__()) / self.featuresMatrix.shape[0]
        vocabularySize = self.featuresMatrix.shape[1]
        wsinc = self.nbWordsInCls[cID]        
        sumLn = math.log(p_of_c)   
        for w in featuresVector:
            if self.words.bag.has_key(w):
                if self.matrix2.retraceWords.has_key(self.words.bag[w][1]):
                    wID = self.matrix2.retraceWords[self.words.bag[w][1]]
                    winc = self.countWordInCls(wID, cID)
                    sumLn += math.log((winc + 1) / (wsinc + vocabularySize)) 
                    sumLn -= math.log((self.countW(wID) - winc + 1) / (self.nbWords - wsinc + vocabularySize))
        return sumLn


    def countWordInCls(self, wID, cID):
        return float(self.featuresMatrix[self.positions[cID], wID + 1].toarray().sum())
        
    
    def countWordsInClass(self, cID):
        return float(self.featuresMatrix[self.positions[cID], 1:].toarray().sum())
            
    
    def countW(self, wID):
        return float(self.featuresMatrix[:, wID + 1].toarray().sum())


    def countWordsInAllClass(self):
        return float(self.featuresMatrix[:, 1:].toarray().sum())


    def getNbWordsInClusters(self):
        nbWordsInCls = {}
        for i in self.clusters.positions:
            nbWordsInCls[i] = self.featuresMatrix[self.clusters.positions[i],1:].toarray().sum()
            
        return nbWordsInCls
            